package com.xiaoxin.experience.system.rate;

import com.xiaoxin.experience.common.BusinessException;
import com.xiaoxin.experience.common.ErrorCode;
import com.xiaoxin.experience.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @author xiaoxin
 */
@Aspect
@Slf4j
@Order(2)
@Component
public class RateLimitAdvance {

    private static final Map<String,RateLimiter> RATE_LIMITER_MAP = new HashMap<>();


    @Pointcut("@annotation(RateLimit)")
    public void executorPointcut()
    {
    }

    @Before(value = "executorPointcut()")
    public void before(JoinPoint joinPoint)
    {
        log.debug("access rate limit check begin");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        RateLimit rateLimit = signature.getMethod().getAnnotation(RateLimit.class);
        String className = joinPoint.getTarget().getClass().getName();
        String defaultId = className + "." + signature.getName();
        RateLimiter rateLimiter = getRateLimiter(rateLimit, defaultId);
        checkPass(rateLimiter);
    }

    private void checkPass(RateLimiter rateLimiter)
    {
        boolean pass = rateLimiter.tryAcquire();
        log.debug("access rate limit check end, check result {}",pass);
        if (pass)
        {
            return;
        }
        throw new BusinessException(ErrorCode.SYSTEM_BUSY,"system busy!");
    }

    private RateLimiter getRateLimiter(RateLimit rateLimit, String defaultId)
    {
        String id = StringUtil.isBlank(rateLimit.key()) ? defaultId : rateLimit.key();
        return RATE_LIMITER_MAP.computeIfAbsent(id,v -> new RateLimiter(rateLimit.rate(),rateLimit.interval()));
    }
}
